﻿//Ham ismiddle tra ve ket qua sai khi check vi tri cua Vua khi di ra xa Hau, Xe hoac tuong.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.IO;

namespace Chess
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public string startpos = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
        public string[] symbol = new string[] { "wk", "wq", "wr", "wb", "wn", "wp", "bk", "bq", "br", "bb", "bn", "bp" };
        public Dictionary<string, bool> States = new Dictionary<string, bool>();

        public Dictionary<string, BitmapImage> pic = new Dictionary<string, BitmapImage>();
        public List<Pieces> listpieces = new List<Pieces>();

        public enum ChessSide { None, White, Black };
        public ChessEngine white = null;
        public ChessEngine black = null;

        public string Passant = "";

        public MainWindow()
        {
            InitializeComponent();
            foreach (string s in symbol)
            {
                Uri u = new Uri("pack://application:,,,/Chess;component/Images/" + s + ".png");
                pic.Add(s, new BitmapImage(u));
            }

            cvBoard.AllowDrop = true;
            cvBoard.Drop += cvBoard_Drop;


            States.Add("WhiteNext", true);
            States.Add("WhiteKS", true);
            States.Add("WhiteQS", true);
            States.Add("BlackKS", true);
            States.Add("BlackQS", true);
            SetBoard(startpos);
        }

        

        void cvBoard_Drop(object sender, DragEventArgs e)
        {

            Image img = (Image)e.Data.GetData(typeof(Image));
            double x = Math.Floor(e.GetPosition(cvBoard).X/50) * 50 ;
            double y = Math.Floor((400 - e.GetPosition(cvBoard).Y )/ 50) * 50;

            string src = _PointToString(new Point(Canvas.GetLeft(img),350 - Canvas.GetBottom(img)));
            string des = _PointToString(new Point(x, 350 - y));
            if (src == des) return;

            DoMove(src, des);
            //if (Turn == engineSide) 
            SendMove();
            
        }
        public bool CheckMate()
        {
            string kingpos = "";
            string king = (Turn == ChessSide.White ? "wk" : "bk");

            foreach (Pieces p in listpieces) if (p.Value == king) { kingpos = p.Position; break; }

            if (CheckKing(kingpos))
            { 
                //try to move king

                //try to eat enemy

                //try to block
            }
            return false;
        }
        public bool CheckKing(string pos)
        {
            char side = (Turn == ChessSide.White ? 'b' : 'w');
            foreach (Pieces p in listpieces)
            {
                if (p.Value[0] == side) 
                {
                    if (IsValidMove(p.Position, pos,false,false,false)) return true; 
                }
            }
            return false;
        }
        public void DoMove(string src, string des)
        {
            string piecesrc = "", piecedes = "";
            foreach (Pieces p in listpieces)
            {
                if (p.Position == src) piecesrc = p.Value;
                if (p.Position == des) piecedes = p.Value;
            }
            if (GetSide(piecesrc) != Turn) return;

            #region check castle
            if (piecesrc == "wk")
            {
                if ((src == "e1") && (des == "c1"))
                {
                    if (States["WhiteQS"])
                    {
                        //Move king to c1
                        foreach (Pieces p in listpieces)
                            if (p.Position == src) { p.Position = des; break; }

                        //Move rook to d1
                        foreach (Pieces p in listpieces) if (p.Position == "a1") { p.Position = "d1"; break; }
                        States["WhiteQS"] = false;
                        States["WhiteKS"] = false;

                        UpdateMoveSequence(src + des);
                        ChangeTurn();
                        return;
                    }
                }
                if ((src == "e1") && (des == "g1"))
                {
                    if (States["WhiteKS"])
                    {
                        //Move king to g1
                        foreach (Pieces p in listpieces)
                            if (p.Position == src) { p.Position = des; break; }
                        //Move rook to d1
                        foreach (Pieces p in listpieces) if (p.Position == "h1") { p.Position = "f1"; break; }
                        States["WhiteKS"] = false;
                        States["WhiteQS"] = false;

                        UpdateMoveSequence(src + des);
                        ChangeTurn();
                        return;
                    }
                }
            }

            if (piecesrc == "bk")
            {
                if ((src == "e8") && (des == "c8"))
                {
                    if (States["BlackQS"])
                    {
                        //Move king to c1
                        foreach (Pieces p in listpieces)
                            if (p.Position == src) { p.Position = des; break; }

                        //Move rook to d1
                        foreach (Pieces p in listpieces) if (p.Position == "a8") { p.Position = "d8"; break; }
                        States["BlackQS"] = false;
                        States["BlackKS"] = false;

                        UpdateMoveSequence(src + des);
                        ChangeTurn();
                        return;
                    }
                }
                if ((src == "e8") && (des == "g8"))
                {
                    if (States["BlackKS"])
                    {
                        //Move king to g1
                        foreach (Pieces p in listpieces)
                            if (p.Position == src) { p.Position = des; break; }
                        //Move rook to d1
                        foreach (Pieces p in listpieces) if (p.Position == "h8") { p.Position = "f8"; break; }
                        States["BlackKS"] = false;
                        States["BlackQS"] = false;

                        UpdateMoveSequence(src + des);
                        ChangeTurn();
                        return;
                    }
                }
            }
            #endregion

            #region check promotion

            if ((piecesrc[1] == 'p') && ((des[1] == '8') || (des[1] == '1')))
            {
                if (IsValidMove(src, des))
                {
                    foreach (Pieces p in listpieces)
                    {
                        if (p.Position == des)
                        {
                            p.Position = "j7";
                            break;
                        }
                    }
                    foreach (Pieces p in listpieces)
                        if (p.Position == src)
                        {
                            p.Position = des;
                            p.Value = p.Value[0] + "q";
                            break;
                        }

                    UpdateMoveSequence(src + des + "q");
                    ChangeTurn();
                    return;
                }
            }
            #endregion

            #region enpasssant
            if ((piecesrc[1] == 'p') && (des == Passant))
            {
                if (CheckPawnEat(piecesrc, src, Passant))
                {
                    string target = "";
                    target = des[0].ToString() + ((char)(piecesrc[0] =='w'?des[1]-1:des[1]+1)).ToString();
                    foreach (Pieces p in listpieces) if (p.Position == target) p.Position = "z7";
                    foreach (Pieces p in listpieces) if (p.Position == src) p.Position = des;
                    
                    Passant = "";
                    UpdateMoveSequence(src + des);
                    ChangeTurn();
                    return;

                }
            }
            #endregion

            #region check valid move

            if (IsValidMove(src, des))
            {

                foreach (Pieces p in listpieces) if (p.Position == des) p.Position = "z7";
                foreach (Pieces p in listpieces) if (p.Position == src) p.Position = des;

                UpdateMoveSequence(src + des);
                //Check en passant
                bool ispassant = false;
                if (piecesrc[1] == 'p')
                {
                    if (src[0] == des[0])
                    {
                        if ((src[1] - des[1] == 2) || (src[1] - des[1] == -2))
                        {
                            Passant = src[0].ToString() + ((char)((src[1] + des[1]) / 2)).ToString();
                            ispassant = true;
                        }
                    }
                }
                if (ispassant == false) Passant = "";
                //
                ChangeTurn();
            }
            #endregion
        }
        
       
        private void imgBoard_MouseMove(object sender, MouseEventArgs e)
        {
            //Point p = e.GetPosition(this.imgBoard);            
            //lbPosition.Content = "Position: " + _PointToString(p);
            Image img = (Image)sender;
            if (img != null && e.LeftButton == MouseButtonState.Pressed)
            {
                DragDrop.DoDragDrop(img,img, DragDropEffects.Move);
            }
            
        }
        public bool IsExist(string pos)
        {
            foreach (Pieces p in listpieces)
                if (p.Position == pos) return true;
            return false;
        }

        public bool IsMiddle(string src, string des)
        {
            int a = src[0] - des[0];
            int b = src[1] - des[1];

            int i = 0;
            int j = 0;
            while (true)
            {
                if (a > 0) i++; if (a < 0) i--;
                if (b > 0) j++; if (b < 0) j--; if ((i == a) && (j == b)) break;
                string mid = ((char)(src[0] - i)).ToString()+ ((src[1] - j - '0')).ToString();
                if (IsExist(mid)) return true;

            }
            return false;
        }

        public bool IsValidMove(string src,string des,bool carecolor = true,bool carecheck = true,bool pawneat = true)
        {
            if (des == src) return false;

            string piecesrc ="", piecedes ="";
            foreach (Pieces p in listpieces)
            {
                if (p.Position == src) piecesrc = p.Value;
                if (p.Position == des) piecedes = p.Value;
            }
            if (carecolor) if (piecedes != "")
            {
                if (piecesrc[0] == piecedes[0])
                {
                    MessageBox.Show("Same color");
                    return false;
                }
            }
            switch (piecesrc[1])
            {
                case 'p':
                    {
                        if (pawneat)
                        {
                            if (piecedes == "")
                            {
                                return CheckPawnMove(piecesrc, src, des);
                            }
                            else
                            {
                                return CheckPawnEat(piecesrc, src, des);
                            }
                        }
                        else return CheckPawnEat(piecesrc, src, des);
                    }
                case 'b': return CheckBishopMove(src, des);
                case 'r': return CheckRookMove(src, des);
                case 'q': return CheckQueenMove(src, des);
                case 'k': return CheckKingMove(src, des,carecheck);
                case 'n': return CheckKnightMove(src, des);
                default: break;
            }
            
    
    
            return false;
        }
        public bool CheckPawnMove(string piecesrc, string src, string des)
        {
            if (piecesrc[0] == 'w')
            {
                if (src[0] == des[0])
                {
                    if (src[1] + 1 == des[1]) return true;
                    else if ((src[1] + 2 == des[1]) && (src[1] == '2')) return true;
                }
            }
            else
            {
                if (src[0] == des[0]) 
                    if (src[1] - 1 == des[1]) return true;
                    else if ((src[1] - 2 == des[1]) && (src[1] == '7')) return true;
            }

            return false;
        }
        public bool CheckPawnEat(string piecesrc, string src, string des)
        {
            if (piecesrc[0] == 'w')
            {
                if (src[1] + 1 == des[1])
                {
                    if ((src[0] - des[0] == 1) || (src[0] - des[0] == -1)) return true;
                    else return false;
                }
            }
            else
            {
                if (src[1] - 1 == des[1])
                {
                    if ((src[0] - des[0] == 1) || (src[0] - des[0] == -1)) return true;
                    else return false;
                }
            }
            return false;
        }
        public bool CheckBishopMove(string src, string des)
        {
            int a = src[0] - des[0];
            int b = src[1] - des[1];

            if ((a == b) || (a == -b))
            {
                if (!IsMiddle(src,des)) return true;
            }
            return false;
        }
        public bool CheckRookMove(string src, string des)
        {
            int a = src[0] - des[0];
            int b = src[1] - des[1];

            if ((a == 0) || (b == 0))
            {
                if (!IsMiddle(src, des)) return true;
            }
            return false;
        }

        public bool CheckQueenMove(string src, string des)
        { 
            return (CheckRookMove(src,des) || CheckBishopMove(src,des));
        }

        public bool CheckKingMove(string src, string des,bool carecheck)
        {
            if (carecheck) if (CheckKing(des)) return false;
            int a = src[0] - des[0];
            int b = src[1] - des[1];
            if ((a >= -1) && (a <= 1) && (b >= -1) && (b <= 1)) return true;

            return false;
        }
        public bool CheckKnightMove(string src, string des)
        {
            int a = src[0] - des[0];
            int b = src[1] - des[1];
            if ((a * b == 2) || (a * b == -2)) return true;
            return false;
        }
        public string _PointToString(Point p)
        {
            char x = (char)((int)p.X / 50 + 97);
            int y = 8 - (int)p.Y / 50;
            return x + y.ToString();
        }

        public void SetBoard(string fen,bool reset = false)
        { 
            char[] sep1 = new char[] {' '};
            char[] sep2 = new char[] {'/'};
            string[,] cells = new string[8, 8];
            cells.Initialize();

            string[] fenelement = fen.Split(sep1);

            if (fenelement[1] == "w") States["WhiteNext"] = true;
            else States["WhiteNext"] = false;

            string castle = fenelement[2];
            if (castle.Contains("K")) States["WhiteKS"] = true; else States["WhiteKS"] = false;
            if (castle.Contains("Q")) States["WhiteQS"] = true; else States["WhiteQS"] = false;
            if (castle.Contains("k")) States["BlackKS"] = true; else States["BlackKS"] = false;
            if (castle.Contains("q")) States["BlackQS"] = true; else States["BlackQS"] = false;

           
            string piecespos = fenelement[0];
            string[] rank = piecespos.Split(sep2);
            for (int i = 0; i < 8; i++)
            {
                int j = 0;
                while (j < 8)
                {
                    switch (rank[i][j])
                    {
                        case 'R': cells[i, j] = "wr"; j++; break;
                        case 'N': cells[i, j] = "wn"; j++; break;
                        case 'K': cells[i, j] = "wk"; j++; break;
                        case 'Q': cells[i, j] = "wq"; j++; break;
                        case 'B': cells[i, j] = "wb"; j++; break;
                        case 'P': cells[i, j] = "wp"; j++; break;

                        case 'r': cells[i, j] = "br"; j++; break;
                        case 'n': cells[i, j] = "bn"; j++; break;
                        case 'k': cells[i, j] = "bk"; j++; break;
                        case 'q': cells[i, j] = "bq"; j++; break;
                        case 'b': cells[i, j] = "bb"; j++; break;
                        case 'p': cells[i, j] = "bp"; j++; break;
                        default:
                            {
                                j += int.Parse(rank[i][j].ToString());
                                break;
                            }
                    }
                }
            }

            int n = 0;
            for (int i = 0; i < 8; i++)
                for (int j = 0; j < 8; j++)
                {
                    if (cells[i, j] != null)
                    {
                        if (reset)
                        {
                            listpieces[n].Value = cells[i, j];
                            listpieces[n].Position = ((char)(j + 'a')).ToString() + (8 - i).ToString();
                            n++;
                        }
                        else
                        {
                            Pieces p = new Pieces(cells[i, j], ((char)(j + 'a')).ToString() + (8 - i).ToString());
                            listpieces.Add(p);
                            p.Pic.MouseMove += imgBoard_MouseMove;
                            cvBoard.Children.Add(p.Pic);
                        }
                    }
                }
        }        

        public class Pieces
        {
            private string _value;
            private string _pos;

            public Image Pic = new Image();

            public Pieces(string symbol, string pos)
            {
                Value = symbol;
                Position = pos;
                Pic.Width = 50;
                Pic.Height = 50;
                
            }

            public Pieces()
            {
            }

            public string Value
            {
                get { return _value; }
                set 
                {
                    _value = value;
                    Uri u = new Uri("pack://application:,,,/Chess;component/Images/" + value + ".png");
                    Pic.Source = new BitmapImage(u); 
                }
            }
            public string Position
            {
                get { return _pos; }
                set
                {
                    _pos = value;
                    Canvas.SetLeft (Pic,(value[0] - 'a') * 50);
                    Canvas.SetBottom(Pic, (value[1] - '1') * 50);
                }
            }

        }

        public class ChessEngine : IDisposable
        {
            public string EngineName
            {
                get { return System.IO.Path.GetFileNameWithoutExtension(Proc.StartInfo.FileName); }
            }
            public Process Proc;

            public ChessEngine(string filename)
            {
                Proc = new System.Diagnostics.Process();
                Proc.StartInfo.CreateNoWindow = true;
                Proc.StartInfo.FileName = filename;
                Proc.StartInfo.RedirectStandardOutput = true;
                Proc.StartInfo.UseShellExecute = false;
                Proc.StartInfo.RedirectStandardInput = true;
                Proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                
            }

            private bool _started = false;
            public bool IsStarted
            {
                get { return _started; }
            }

            public void Start()
            {
                Proc.Start();
                Proc.BeginOutputReadLine();
                _started = true;
            }
            public void Stop()
            {
                Proc.CancelOutputRead();
                Proc.Kill();
                _started = false;
            }

            public override string ToString()
            {
                string s = (IsStarted ? "@" : "");
                return s + EngineName;
            }

            void IDisposable.Dispose()
            {
                Proc.Kill();
            }
        }
        
        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            if (Turn == ChessSide.None)
            {
                StartGame(ChessSide.White);
                if (white != null) SendMove(white);
                btnStart.Content = "Stop";
                lbTurn.Content = "Turn: " + Turn.ToString();
            }
            else
            {
                Turn = ChessSide.None;
                btnStart.Content = "Start";
                SetBoard(startpos, true);
                lbTurn.Content = "Turn: " + Turn.ToString();
                MoveSequence = "";
                txtOutput.Text = "";
            }
        }

        public ChessSide Turn = ChessSide.None;        
        public string MoveSequence = "";
        public int Depth = 15;
        public ChessSide engineSide = ChessSide.Black;

        public void StartGame(ChessSide side)
        {
            Turn = side;
        }
        public ChessSide GetSide(string p)
        {
            if (p[0] == 'w') return ChessSide.White;
            if (p[0] == 'b') return ChessSide.Black;
            return ChessSide.None;
        }
        public ChessSide GetSideFromPos(string pos)
        {
            foreach (Pieces p in listpieces)
                if (p.Position == pos) return  GetSide(p.Value);
            return ChessSide.None;
        }
        public void ChangeTurn()
        {
            if (Turn == ChessSide.White) Turn = ChessSide.Black; 
            else if (Turn == ChessSide.Black) Turn = ChessSide.White;

            lbTurn.Content = "Turn: " + Turn.ToString();
        }
        public void UpdateMoveSequence(string s)
        {
            MoveSequence = (MoveSequence == ""?s:MoveSequence + " " + s);

            listMove.Items.Clear();
            char[] sep = new char[] {' '};
            string[] moves = MoveSequence.Split(sep);
            int len = moves.Length;
            int i = 0;
            while (i < len)
            {
                string tmp = (i / 2 + 1).ToString() + ". ";
                tmp += moves[i];
                i++;
                if (i < len) tmp += " " + moves[i];
                listMove.Items.Add(tmp);
                i++;
            }

        }

        public void ParseOutput(string output)
        {
            char[] sep = new char[] { ' ' };
            string[] str = output.Split(sep);

            if (str[0] == "bestmove")
            {
                txtOutput.Text = str[1] + " (=>" + str[3] + ")\n" + txtOutput.Text;

                string src = str[1][0].ToString() + str[1][1];
                string des = str[1][2].ToString() + str[1][3];
                if (!Assistmode)
                {
                    DoMove(src, des);

                    SendMove();
                }
            }
            
        }
        public void SendMove(ChessEngine engine)
        {
            if (engine == null) return;
            engine.Proc.StandardInput.WriteLine("position startpos moves " + MoveSequence);
            //engine.StandardInput.WriteLine("go depth " + Depth);
            engine.Proc.StandardInput.WriteLine("go movetime 3000");
        }

        public void SendMove()
        {
            if (Turn == ChessSide.None) return;

            ChessEngine engine = null;
            if (Turn == ChessSide.White)
            {
                if (comboBox1.Text != "Human") engine = engines[comboBox1.Text];
                else return;
            }
            else if (Turn == ChessSide.Black)
            {
                if (comboBox2.Text != "Human") engine = engines[comboBox2.Text];
                else return;
            }

            engine.Proc.StandardInput.WriteLine("position startpos moves " + MoveSequence);
            //engine.StandardInput.WriteLine("go depth " + Depth);
            engine.Proc.StandardInput.WriteLine("go movetime 3000");
        }

        public Dictionary<string,ChessEngine> engines = new Dictionary<string,ChessEngine>();

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            string[] enginefiles = Directory.GetFiles("Engine","*.exe");

            foreach (string s in enginefiles)
            {
                ChessEngine c = new ChessEngine(s);
                c.Proc.OutputDataReceived += Proc_OutputDataReceived;
                engines.Add(c.EngineName,c);
                listBox1.Items.Add(c);
            }
            
        }

        void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null && e.Data != "")
            {
                string line = e.Data.ToString().Trim();
                this.Dispatcher.BeginInvoke(new Action(() => ParseOutput(line)), null);
                
            }
        }

        private void btnStartEngine_Click(object sender, RoutedEventArgs e)
        {
            if (listBox1.SelectedIndex != -1)
            {
                ChessEngine c = (ChessEngine)listBox1.SelectedItem;
                if (c.IsStarted)
                {
                    c.Stop();
                    listBox1.Items.Refresh();
                    comboBox1.Items.Remove(c.EngineName);
                    comboBox2.Items.Remove(c.EngineName);

                    btnStartEngine.Content = "Start";
                }
                else
                {
                    c.Start();
                    listBox1.Items.Refresh();
                    comboBox1.Items.Add(c.EngineName);
                    comboBox2.Items.Add(c.EngineName);
                    btnStartEngine.Content = "Stop";
                }
            }
        }

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (listBox1.SelectedIndex != -1)
            {
                if (((ChessEngine)listBox1.SelectedItem).IsStarted) btnStartEngine.Content = "Stop";
                else btnStartEngine.Content = "Start";
            }
        }

        public bool Assistmode = false;
        private void ckAssist_Checked(object sender, RoutedEventArgs e)
        {
            Assistmode = true;
        }

        private void ckAssist_Unchecked(object sender, RoutedEventArgs e)
        {
            Assistmode = false;
        }
        
    }
}
